package core

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"
)

type BlockChain struct {
	Nodes []string //区块链网络中的每个节点
	Chain []Blcok //所有块
	CurrentTransactions []Transaction //当前区块未打包交易
	Length int64 //区块长度
}

//初始化区块链
func NewBlockChain() *BlockChain  {
	blockChain := &BlockChain{}
	//构建创始区块
	blockChain.Chain =  append(blockChain.Chain,*blockChain.NewBlock(int64(100)))
	return blockChain
}


//通过工作量证明构建新的区块
func (this BlockChain) NewBlock(proof int64) *Blcok {
	newBlcok := &Blcok{
		Index: len(this.Chain) + 1,
		TimeStamp: time.Now().UnixNano(),
		//打包交易账本
		Transactions: this.CurrentTransactions,
		Proof: proof,
		PreviousHash: Hash(this.LastBlock()),
	}

	return newBlcok
}

//计算hash
func Hash(block *Blcok) string {
	data ,_:=json.Marshal(block)
	hash := sha256.New()
	hash.Write(data)
	return hex.EncodeToString(hash.Sum(nil))
}

//获取上一个区块
func (this BlockChain) LastBlock() *Blcok {
	if len(this.Chain) == 0 {
		//初始化创世区块
		return nil
	}
	return &this.Chain[len(this.Chain) -1]
}

//新增交易、记账
func (this *BlockChain)NewTransaction(sender string, recipient string,amount float64)   {
	newTransaction := &Transaction{
		Sender: sender,
		Recipient: recipient,
		Amount: amount,
	}

	this.CurrentTransactions = append(this.CurrentTransactions,*newTransaction)
	//返回喜爱个将要被挖出来的区块的高度
	//return int64(this.LastBlock().Index + 1)
}

//工作量证明，拿上一个区块的工作量做运算，满足条件
func (this *BlockChain)ProofOfWork(lastProof int64)int64  {
	var proof int64 = 0
	for  {
		if !ValidProof(lastProof,proof) {
			proof++
		}else {
			break
		}
	}
	return proof
}

//判断是否有效工作量，目前比特币是计算hash是否是8个0
func ValidProof(lastProof int64, proof int64) bool {
	temp := string(lastProof) + string(proof)

	hash := sha256.New()
	hash.Write([]byte(temp))
	guessHash := hex.EncodeToString(hash.Sum(nil))

	rs := []rune(guessHash)

	return string(rs[0:4]) == "0000"
}

//注册节点
func (this *BlockChain)RegisterNode(address string) bool  {
	u,err := url.Parse(address)

	if err != nil {
		fmt.Println("add node come up err",err)
		return  false
	}
	this.Nodes = append(this.Nodes,u.Host)
	return true
}

//解决节点之间的冲突，共识
func (this *BlockChain)ResolveConflict()bool  {
	//获取链中注册的及诶单列表
	neighbors := this.Nodes

	//当前链长度
	maxLength := len(this.Chain)

	//保存最长的链
	var newChain []Blcok
	for _, value := range neighbors {
		var url string = "http://"+ value +"/chain"
		resp,err := http.Get(url)
		if err != nil {
			log.Println("ResolveConflict node :"+ value + "error",err)
			return false
		}
		body, err :=ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		
		//解析邻近节点数据
		var tempBlockChain BlockChain
		json.Unmarshal(body,&tempBlockChain)
		
		tempChain := tempBlockChain.Chain
		tempLength := len(tempChain)
		//如果临近节点链长度大于当前链的长度，并且是有效的链，就替换当前的链
		if tempLength > maxLength  && this.validChain(tempChain){
			maxLength = tempLength
			newChain = tempChain
		}
		
	}

	if newChain != nil {
		this.Chain = newChain
		this.Length = int64(maxLength)
		return true
	}else {
		return false
	}
}


//判断链是否有效，主要是工作量和hash校验
func (this *BlockChain)validChain(chain []Blcok) bool  {
	lastBlock := chain[0]
	//创始区块不需要验证
	currentIndex := 1
	for currentIndex < len(chain) {
		block := chain[currentIndex]

		//判断当前区块记录上一个区块的hash合计算出来的上去刽的hash是否相等
		if block.PreviousHash != Hash(&lastBlock) {
			return false
		}

		//判断工作量证明是否满足挖矿的要求
		if !ValidProof(lastBlock.Proof, block.Proof) {
			return false
		}

		lastBlock = block
		currentIndex = currentIndex + 1
	}
	return true
}


//返回所哟节点
func (this *BlockChain)GetNodes()string  {
	nodes ,_ :=json.Marshal(this.Nodes)
	return string(nodes)
}

func (this *BlockChain)BlockChainToJson()string  {
	data,_ := json.Marshal(this)
	return string(data)
}


























